This is a brief comparison of definitions for “middle neighborhoods” in Baltimore. The two definitions being compared are:

U.S. Census data being used are the 2012-2016 (5-year) American Community Survey estimates.

Are there any other definitions we should consider?

# neighborhood boundaries
hoods.url <- "https://data.baltimorecity.gov/resource/h3fx-54q3.geojson"
# surely there is a better way, and i tried, but couldn't get anything else to work.
# from geojson to sf to spatial df. geojson_sp didn't work for me.
hoods <- geojson_sf(hoods.url)
incomplete final line found on 'https://data.baltimorecity.gov/resource/h3fx-54q3.geojson'
hoods <- as_Spatial(hoods)
hoods <- spTransform(hoods, CRS("+init=epsg:4326"))
# HUD area median income for Baltimore-Columbia-Towson
ami.2016 <- 86700
#ami.2018 <- 94900
hmt@data <- hmt@data %>% mutate(
  
  # assign healthy, middle, and distressed based on HMT
  hmt.tier = case_when(
    MVA17HrdCd %in% c("A", "B", "C") ~ "healthy",
    MVA17HrdCd %in% c("D", "E", "F", "G", "H") ~ "middle",
    MVA17HrdCd %in% c("I", "J") ~ "distressed",
    TRUE ~ "other"),
  
  # HMT again, but with middle broken in two
  hmt.tier.2mid = case_when(
    MVA17HrdCd %in% c("A", "B", "C") ~ "healthy",
    MVA17HrdCd %in% c("D", "E") ~ "upper middle",
    MVA17HrdCd %in% c("F", "G", "H") ~ "lower middle",
    MVA17HrdCd %in% c("I", "J") ~ "distressed",
    TRUE ~ "other"),
  # assign healthy, middle, and distressed based on block group median income
    med.inc.tier = 
    case_when(
      (hmt.tier != "other") & 
        (Median_Household_Income.2016 > 1.25 * ami.2016) ~ "healthy",
      (hmt.tier != "other") & 
        (Median_Household_Income.2016 < 0.75 * ami.2016) ~ "distressed",
      (hmt.tier == "other") ~ NA_character_,
      TRUE ~ "middle"
    )
  )

Middle Neighborhoods - HMT Definiton

This map shows only middle neighborhoods based on HMT (includes only typologies D through H). Black borders and labels-on-hover are for neighborhoods. At first glance, this removes neighborhoods within the “white L” and “black butterfly” neighborhoods of Baltimore, with most everything else remaining.

mid.hoods.hmt <- subset(hmt, hmt.tier == "middle")
leaflet() %>% 
  setView(lng = -76.6, lat = 39.3, zoom = 11) %>%
  addProviderTiles(providers$Stamen.TonerLite) %>%
  addPolygons(data = mid.hoods.hmt, 
              color = iteam.colors[1],
              weight = 1,
              #fillColor = ~pal(MVA17HrdCd),
              fillColor = iteam.colors[1],
              fillOpacity = .6,
              #opacity = 0,
              label = ~as.character(mid.hoods.hmt$MVA17HrdCd)
              ) %>%
  #addGeoJSON(hoods, weight = 1, color = "black", fillOpacity = 0)
  addPolygons(data = hoods, 
              weight = 2, 
              color = "black",
              opacity = 0.5,
              fillOpacity = 0, 
              label = ~hoods$label)

Breaking “middle” into “lower middle” (F, G, H) and “upper middle” (D, E) and including “healthy” and "distressed:

#mid.hoods.hmt.2mid <- subset(hmt, hmt.tier.2mid %in% c("lower middle", "upper middle"))
hmt.2mid.pal <- colorFactor(
  domain = hmt$hmt.tier.2mid,
  palette = c(iteam.colors[4],
              iteam.colors[3],
              "#f4d57f", 
              NA,
              iteam.colors[1])
)
leaflet() %>% 
  setView(lng = -76.6, lat = 39.3, zoom = 11) %>%
  addProviderTiles(providers$Stamen.TonerLite) %>%
  addPolygons(data = hmt, 
              color = iteam.colors[1],
              weight = 1,
              fillColor = ~hmt.2mid.pal(hmt.tier.2mid),
              #fillColor = iteam.colors[1],
              fillOpacity = .6,
              #opacity = 0,
              label = ~as.character(hmt$MVA17HrdCd)
              ) %>%
  #addGeoJSON(hoods, weight = 1, color = "black", fillOpacity = 0)
  addPolygons(data = hoods, 
              weight = 2, 
              color = "black",
              opacity = 0.5,
              fillOpacity = 0, 
              label = ~hoods$label)

Middle Neighborhoods - Median Income Definition

This map shows only middle neighborhoods based on the median income definition - that is, block groups where the household median income is between 75% and 125% of the area median income (Baltimore-Columbia-Towson for 2016). Qualitatively, this results in a more sparse and disjointed definition of middle neighborhoods.

mid.hoods.med.inc <- subset(hmt, med.inc.tier == "middle")
leaflet() %>% 
  setView(lng = -76.6, lat = 39.3, zoom = 11) %>%
  addProviderTiles(providers$Stamen.TonerLite) %>%
  addPolygons(data = mid.hoods.med.inc, 
              color = iteam.colors[3],
              weight = 1,
              #fillColor = ~pal(MVA17HrdCd),
              fillColor = iteam.colors[3],
              fillOpacity = .6,
              #opacity = 0,
              label = ~as.character(mid.hoods.med.inc$MVA17HrdCd)
              ) %>%
  #addGeoJSON(hoods, weight = 1, color = "black", fillOpacity = 0)
  addPolygons(data = hoods, 
              weight = 2, 
              color = "black",
              opacity = 0.5,
              fillOpacity = 0, 
              label = ~hoods$label)

Comparison of Definitions

The map below overlays the two definitions.

leaflet() %>% 
  setView(lng = -76.6, lat = 39.3, zoom = 11) %>%
  addProviderTiles(providers$Stamen.TonerLite) %>%
  addPolygons(data = mid.hoods.med.inc, 
              color = iteam.colors[3],
              weight = 1,
              #fillColor = ~pal(MVA17HrdCd),
              fillColor = iteam.colors[3],
              fillOpacity = .7,
              #opacity = 0,
              label = ~as.character(mid.hoods.med.inc$MVA17HrdCd)) %>%
  addPolygons(data = mid.hoods.hmt, 
              color = iteam.colors[1],
              weight = 1,
              #fillColor = ~pal(MVA17HrdCd),
              fillColor = iteam.colors[1],
              fillOpacity = .5,
              #opacity = 0,
              label = ~as.character(mid.hoods.hmt$MVA17HrdCd)) %>%
  addPolygons(data = hoods, 
              weight = 2, 
              color = "black",
              opacity = 0.5,
              fillOpacity = 0, 
              label = ~hoods$label) %>%
  addLegend("bottomright",
            colors = c(iteam.colors[1], iteam.colors[3]),
            labels = c("HMT", "Median Income"))

Below is a table comparing the two definitions based on the number of block groups and population in each tier. About 55% of Baltimore residents live in a middle neighborhood based on the HMT definition. However, only about 14% of the city’s population makes between 75% and 125% of AMI. 75% of the city population lives in a block group with median income below 75% of AMI. (Use the right triangle arrow at the right side of the table heading to see additional columns.)

hmt.summary <- hmt@data %>% 
  group_by(hmt.tier) %>%
  summarise(n.block.groups.hmt = n(),
            pop.hmt = sum(Population.2016)) %>%
  mutate(pct.block.groups.hmt = percent(n.block.groups.hmt / sum(n.block.groups.hmt)),
         pct.pop.hmt = percent(pop.hmt / sum(pop.hmt)))
med.inc.summmary <- hmt@data %>% 
  group_by(med.inc.tier) %>%
  summarise(n.block.groups.med.inc = n(),
            pop.med.inc = sum(Population.2016)) %>%
  mutate(pct.block.groups.med.inc = percent(n.block.groups.med.inc / sum(n.block.groups.med.inc)),
         pct.pop.med.inc = percent(pop.med.inc / sum(pop.med.inc)))
left_join(hmt.summary, med.inc.summmary, by = c("hmt.tier" = "med.inc.tier")) %>%
  rename(tier = "hmt.tier") %>%
  filter(tier != "other")

Incomes in HMT Middle Neighborhoods

The table below shows the combination of neighborhood housing market tier (based on HMT) and median income levels in each market tier (below 75% AMI, between 75% and 125% AMI, more than 125% AMI).

AMI used is $86,700 based on HUD’s FY16 figure for Baltimore-Columbia-Towson. For FY18, the number is $94,400, however the median income data being used in this analysis is the 5-year ACS estimates ending in 2016.

hmt@data <- hmt@data %>% mutate(
  
  inc.bracket = case_when(
    Median_Household_Income.2016 > 1.25 * ami.2016 ~ "above 125% AMI",
    Median_Household_Income.2016 < 0.75 * ami.2016 ~ "below 75% AMI",
    TRUE ~ "between 75% and 125% AMI"),
  
  tier.inc.bracket = as.character(paste0(hmt.tier.2mid, " | ", inc.bracket))
  
)
hmt@data %>%
  group_by(tier.inc.bracket) %>%
  summarise(n.block.groups = n(),
            pop = sum(Population.2016)) %>%
  mutate(pct.city.pop = percent(pop / sum(pop)))

Interesting to note that all housing market tiers include the low and middle median income ranges, but only the healthy HMT tier includes block groups with median incomes over 125% of AMI.

Here’s an alarming nugget: 55% of the city population is in a middle neighborhood , and 90% of the middle neighborhood population lives in a block group with a median income less than 75% AMI:

hmt@data %>%
  filter(hmt.tier == "middle") %>%
  group_by(tier.inc.bracket) %>%
  summarise(n.block.groups = n(),
            pop = sum(Population.2016)) %>%
  mutate(pct = percent(pop / sum(pop)))

Middle Neighborhoods with Low or High Incomes

(No middle neighborhood block groups with >125% AMI.)

tier.income.subset <- subset(hmt,  
                             hmt.tier.2mid %in% c("lower middle", "upper middle") &
                             inc.bracket %in% c("above 125% AMI", "below 75% AMI") )
tier.inc.pal <- colorFactor(
  domain = tier.income.subset$tier.inc.bracket,
  palette = c(iteam.colors[1],
              iteam.colors[3])
)
leaflet() %>% 
  setView(lng = -76.6, lat = 39.3, zoom = 11) %>%
  addProviderTiles(providers$Stamen.TonerLite) %>%
  addPolygons(data = tier.income.subset, 
              color = iteam.colors[2],
              weight = 1,
              fillColor = ~tier.inc.pal(tier.inc.bracket),
              #fillColor = iteam.colors[1],
              fillOpacity = 0.7,
              #opacity = 0,
              label = ~as.character(paste0(tier.income.subset$tier.inc.bracket))
              ) %>%
  #addGeoJSON(hoods, weight = 1, color = "black", fillOpacity = 0)
  addPolygons(data = hoods,
             weight = 2,
             color = "black",
             opacity = 0.5,
             fillOpacity = 0,
             label = ~hoods$label) %>%
  addLegend("bottomright",
            colors = c(iteam.colors[1], iteam.colors[3]),
            labels = c("lower middle | below 75% AMI", "upper middle | below 75% AMI"))

Healthy and Distressed Neighborhoods with Moderate Income

tier.income.subset <- subset(
  hmt,  
  tier.inc.bracket %in% c("distressed | between 75% and 125% AMI",
                          "healthy | between 75% and 125% AMI")
)
tier.inc.pal <- colorFactor(
  domain = tier.income.subset$tier.inc.bracket,
  palette = c(iteam.colors[4],
              iteam.colors[5])
)
leaflet() %>% 
  setView(lng = -76.6, lat = 39.3, zoom = 11) %>%
  addProviderTiles(providers$Stamen.TonerLite) %>%
  addPolygons(data = tier.income.subset, 
              color = iteam.colors[2],
              weight = 1,
              fillColor = ~tier.inc.pal(tier.inc.bracket),
              #fillColor = iteam.colors[1],
              fillOpacity = 0.7,
              #opacity = 0,
              label = ~as.character(paste0(tier.income.subset$tier.inc.bracket))
              ) %>%
  #addGeoJSON(hoods, weight = 1, color = "black", fillOpacity = 0)
  addPolygons(data = hoods,
             weight = 2,
             color = "black",
             opacity = 0.5,
             fillOpacity = 0,
             label = ~hoods$label) %>%
  addLegend("bottomright",
            colors = c(iteam.colors[4], iteam.colors[5]),
            labels = c("distressed | between 75% and 125% AMI", 
                       "healthy | between 75% and 125% AMI"))

Healthy Neighborhoods with Low Median Income

tier.income.subset <- subset(
  hmt,  
  tier.inc.bracket %in% c("healthy | below 75% AMI")
)
tier.inc.pal <- colorFactor(
  domain = tier.income.subset$tier.inc.bracket,
  palette = c(iteam.colors[5])
)
leaflet() %>% 
  setView(lng = -76.6, lat = 39.3, zoom = 11) %>%
  addProviderTiles(providers$Stamen.TonerLite) %>%
  addPolygons(data = tier.income.subset, 
              color = iteam.colors[2],
              weight = 1,
              fillColor = ~tier.inc.pal(tier.inc.bracket),
              #fillColor = iteam.colors[1],
              fillOpacity = 0.7,
              #opacity = 0,
              label = ~as.character(paste0(tier.income.subset$tier.inc.bracket))
              ) %>%
  #addGeoJSON(hoods, weight = 1, color = "black", fillOpacity = 0)
  addPolygons(data = hoods,
             weight = 2,
             color = "black",
             opacity = 0.5,
             fillOpacity = 0,
             label = ~hoods$label) %>%
  addLegend("bottomright",
            colors = c(iteam.colors[5]),
            labels = c("healthy | below 75% AMI"))

Characteristics of Middle Neighborhoods

Using the HMT definition for middle neighborhoods, the following are looks at block group characteristics grouped by tier.

Because the HMT uses a cluster analysis that includes several housing variables, we’d expect there to be an overlap in the median income distributions because the tiers were not defined by median income.

hmt@data %>% 
  filter(hmt.tier != "other",
         !is.na(Median_Household_Income.2016)) %>% 
  ggplot(aes(Median_Household_Income.2016)) +
  geom_freqpoly(aes(color = hmt.tier), bins = 50) +
  theme_iteam_presentations() +
  labs(
    title = paste0("Block Group Median Household Income\n",
                   "Distribution by HMT Market tier"),
    subtitle = "",
    x = "Median Household Income",
    y = "Count",
    caption = paste0("tier based on 2017 HMT analysis; ",
                     "median household incomes from 2012-2016 ACS data.")
  ) +
  geom_vline(aes(xintercept = ami.2016), color = iteam.colors[2]) +
  annotate("text", x = ami.2016 + 2500, y = 25, 
           label = paste0("Baltimore-Columbia-Towson 2016 AMI:\n", dollar(ami.2016)),
           hjust = 0) +
  theme(plot.caption = element_text(hjust = 1, face = "plain"))

Difference in median incomes between upper and lower middle neighborhoods.

hmt@data %>% 
  filter(hmt.tier != "other",
         !is.na(Median_Household_Income.2016),
         hmt.tier == "middle") %>% 
  ggplot(aes(Median_Household_Income.2016)) +
  geom_freqpoly(aes(color = hmt.tier.2mid), bins = 50) +
  theme_iteam_presentations() +
  labs(
    title = paste0("Middle Neighborhood\nBlock Group Median Household Income\n",
                   "Distribution by HMT Market tier"),
    subtitle = "",
    x = "Median Household Income",
    y = "Count",
    caption = paste0("tier based on 2017 HMT analysis; ",
                     "median household incomes from 2012-2016 ACS data.")
  ) +
  geom_vline(aes(xintercept = ami.2016), color = iteam.colors[2]) +
  annotate("text", x = ami.2016 - 2500, y = 16, 
           label = paste0("Baltimore-Columbia-Towson 2016 AMI:\n", dollar(ami.2016)),
           hjust = 1) +
  theme(plot.caption = element_text(hjust = 1, face = "plain"),
        legend.title = element_blank())

Middle neighborhoods do seem to skew towards older homeownership.

hmt@data %>% 
  filter(hmt.tier != "other") %>% 
  ggplot(aes(Percent_Homeowner_Over_65.2016)) +
  geom_freqpoly(aes(color = hmt.tier), stat = "density") +
  theme_iteam_presentations() +
  labs(
    title = "% Homeowners 65+ by HMT Market tier",
    subtitle = "",
    x = "% Homeowners 65+ y.o.",
    y = "Density",
    caption = paste0("tier based on 2017 HMT analysis; ",
                     "% homeowners 65 and over from 2012-2016 ACS data.")
  ) +
  theme(plot.caption = element_text(hjust = 1, face = "plain"))

Below is the average block group percentage of homeowners that are over the age of 65.

hmt@data %>%
  filter(hmt.tier != "other") %>%
  group_by(hmt.tier) %>%
  summarise(mean.pct.ho.over65 = mean(Percent_Homeowner_Over_65.2016))

While about 54% of the city population lives in middle neighborhoods, 65% of the households with a homeowner 65 years of age or older live in a middle neighborhood.

hmt@data %>%
  filter(hmt.tier != "other") %>%
  mutate(hh = Population.2016 / Household_Size.2016,
         hh.own = hh * Percent_Owner.2016,
         hh.owner.over65 = hh.own * Percent_Homeowner_Over_65.2016) %>%
  group_by(hmt.tier) %>%
  summarise(hh.owner.over65 = sum(hh.owner.over65)) %>%
  mutate(pct.city.owner.over65 = percent(hh.owner.over65 / sum(hh.owner.over65)))
LS0tDQp0aXRsZTogIldoYXQgaXMgYSBNaWRkbGUgTmVpZ2hib3Job29kPyINCiNzdWJ0aXRsZTogDQphdXRob3I6ICJKdXN0aW4gRWxzemFzeiINCmRhdGU6ICJGZWJydWFyeSAzLCAyMDE5Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiAyDQogICAgdG9jX2Zsb2F0OiBmYWxzZQ0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KLS0tDQoNClRoaXMgaXMgYSBicmllZiBjb21wYXJpc29uIG9mIGRlZmluaXRpb25zIGZvciAibWlkZGxlIG5laWdoYm9yaG9vZHMiIGluIEJhbHRpbW9yZS4gVGhlIHR3byBkZWZpbml0aW9ucyBiZWluZyBjb21wYXJlZCBhcmU6DQoNCi0gVS5TLiBDZW5zdXMgYmxvY2sgZ3JvdXBzIHdpdGggKipob3VzaW5nIG1hcmtldCB0eXBvbG9naWVzIEQgdGhyb3VnaCBIKiogYmFzZWQgb24gdGhlIFsyMDE3IEhvdXNpbmcgTWFya2V0IFR5cG9sb2d5XShodHRwczovL3BsYW5uaW5nLmJhbHRpbW9yZWNpdHkuZ292L21hcHMtZGF0YS9ob3VzaW5nLW1hcmtldC10eXBvbG9neSkgYW5hbHlzaXMgKGhlYWx0aHkgPSBBLCBCLCBDOyBkaXN0cmVzc2VkID0gSSwgSikNCi0gVS5TLiBDZW5zdXMgYmxvY2sgZ3JvdXBzIHdpdGggKiptZWRpYW4gaG91c2Vob2xkIGluY29tZSBiZXR3ZWVuIDc1JSBhbmQgMTI1JSBvZiB0aGUgY2l0eSdzIGFyZWEgbWVkaWFuIGluY29tZSoqDQoNClUuUy4gQ2Vuc3VzIGRhdGEgYmVpbmcgdXNlZCBhcmUgdGhlIDIwMTItMjAxNiAoNS15ZWFyKSBBbWVyaWNhbiBDb21tdW5pdHkgU3VydmV5IGVzdGltYXRlcy4NCg0KQXJlIHRoZXJlIGFueSBvdGhlciBkZWZpbml0aW9ucyB3ZSBzaG91bGQgY29uc2lkZXI/DQoNCmBgYHtyIGdsb2JhbF9vcHRpb25zLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTYsDQogICAgICAgICAgICAgICAgICAgICAgZWNobz1GLCB3YXJuaW5nPUYsIG1lc3NhZ2U9RiwgaW5jbHVkZT1UKQ0KYGBgDQoNCmBgYHtyIGluY2x1ZGUgPSBGfQ0KbGlicmFyeShyZWFkeGwpDQojbGlicmFyeShST0RCQykNCmxpYnJhcnkobWFwdG9vbHMpDQpsaWJyYXJ5KGxlYWZsZXQpDQpsaWJyYXJ5KHJnZGFsKQ0KbGlicmFyeShyZ2VvcykNCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KGh0dHIpDQpsaWJyYXJ5KGpzb25saXRlKQ0KbGlicmFyeShnZW9qc29uc2YpDQpsaWJyYXJ5KHJhc3RlcikNCmBgYA0KDQpgYGB7ciBpbmNsdWRlPUZ9DQpzb3VyY2UoIi4uL2NvZGUvMDBfaW5pdGlhbGl6ZS5SIikNCg0KaG10IDwtIGdldF9ibG9ja19ncm91cF9kYXRhKCkNCiMgcHVsbCBITVQgbGF5ZXIgZnJvbSBTUUwgc2VydmVyDQojIHNlcnZlciA8LSAgIHBhc3RlMCgnTVNTUUw6c2VydmVyPWJhbHQtZ3NsaXN0ZW5lcjsnLA0KIyAgICAgICAgICAgICAgICAgICAgJ3VpZD1lZ2lzX3JlYWRlcjsnLA0KIyAgICAgICAgICAgICAgICAgICAgJ3B3ZD1iYWx0aW1vcmUwMTsnLA0KIyAgICAgICAgICAgICAgICAgICAgJ2RhdGFiYXNlPWhvdXNpbmc7JywNCiMgICAgICAgICAgICAgICAgICAgICd0cnVzdGVkX2Nvbm5lY3Rpb249Tm8nKQ0KIyANCiMgaG10IDwtIHJlYWRPR1Ioc2VydmVyLCAiaG91c2luZy5ITVQyMDE3IikNCiMgDQojIGhtdCA8LSBTcGF0aWFsUG9seWdvbnNEYXRhRnJhbWUoaG10LCBobXRAZGF0YSkNCiMgaG10QHByb2o0c3RyaW5nIDwtIENSUygiK2luaXQ9ZXBzZzoyMjQ4IikNCiMgaG10IDwtIHNwVHJhbnNmb3JtKGhtdCwgQ1JTKCIraW5pdD1lcHNnOjQzMjYiKSkNCmBgYA0KDQpgYGB7cn0NCiMgbmVpZ2hib3Job29kIGJvdW5kYXJpZXMNCmhvb2RzLnVybCA8LSAiaHR0cHM6Ly9kYXRhLmJhbHRpbW9yZWNpdHkuZ292L3Jlc291cmNlL2gzZngtNTRxMy5nZW9qc29uIg0KDQojIHN1cmVseSB0aGVyZSBpcyBhIGJldHRlciB3YXksIGFuZCBpIHRyaWVkLCBidXQgY291bGRuJ3QgZ2V0IGFueXRoaW5nIGVsc2UgdG8gd29yay4NCiMgZnJvbSBnZW9qc29uIHRvIHNmIHRvIHNwYXRpYWwgZGYuIGdlb2pzb25fc3AgZGlkbid0IHdvcmsgZm9yIG1lLg0KaG9vZHMgPC0gZ2VvanNvbl9zZihob29kcy51cmwpDQpob29kcyA8LSBhc19TcGF0aWFsKGhvb2RzKQ0KaG9vZHMgPC0gc3BUcmFuc2Zvcm0oaG9vZHMsIENSUygiK2luaXQ9ZXBzZzo0MzI2IikpDQpgYGANCg0KYGBge3J9DQojIEhVRCBhcmVhIG1lZGlhbiBpbmNvbWUgZm9yIEJhbHRpbW9yZS1Db2x1bWJpYS1Ub3dzb24NCmFtaS4yMDE2IDwtIDg2NzAwDQojYW1pLjIwMTggPC0gOTQ5MDANCg0KaG10QGRhdGEgPC0gaG10QGRhdGEgJT4lIG11dGF0ZSgNCiAgDQogICMgYXNzaWduIGhlYWx0aHksIG1pZGRsZSwgYW5kIGRpc3RyZXNzZWQgYmFzZWQgb24gSE1UDQogIGhtdC50aWVyID0gY2FzZV93aGVuKA0KICAgIE1WQTE3SHJkQ2QgJWluJSBjKCJBIiwgIkIiLCAiQyIpIH4gImhlYWx0aHkiLA0KICAgIE1WQTE3SHJkQ2QgJWluJSBjKCJEIiwgIkUiLCAiRiIsICJHIiwgIkgiKSB+ICJtaWRkbGUiLA0KICAgIE1WQTE3SHJkQ2QgJWluJSBjKCJJIiwgIkoiKSB+ICJkaXN0cmVzc2VkIiwNCiAgICBUUlVFIH4gIm90aGVyIiksDQogIA0KICAjIEhNVCBhZ2FpbiwgYnV0IHdpdGggbWlkZGxlIGJyb2tlbiBpbiB0d28NCiAgaG10LnRpZXIuMm1pZCA9IGNhc2Vfd2hlbigNCiAgICBNVkExN0hyZENkICVpbiUgYygiQSIsICJCIiwgIkMiKSB+ICJoZWFsdGh5IiwNCiAgICBNVkExN0hyZENkICVpbiUgYygiRCIsICJFIikgfiAidXBwZXIgbWlkZGxlIiwNCiAgICBNVkExN0hyZENkICVpbiUgYygiRiIsICJHIiwgIkgiKSB+ICJsb3dlciBtaWRkbGUiLA0KICAgIE1WQTE3SHJkQ2QgJWluJSBjKCJJIiwgIkoiKSB+ICJkaXN0cmVzc2VkIiwNCiAgICBUUlVFIH4gIm90aGVyIiksDQoNCiAgIyBhc3NpZ24gaGVhbHRoeSwgbWlkZGxlLCBhbmQgZGlzdHJlc3NlZCBiYXNlZCBvbiBibG9jayBncm91cCBtZWRpYW4gaW5jb21lDQogICAgbWVkLmluYy50aWVyID0gDQogICAgY2FzZV93aGVuKA0KICAgICAgKGhtdC50aWVyICE9ICJvdGhlciIpICYgDQogICAgICAgIChNZWRpYW5fSG91c2Vob2xkX0luY29tZS4yMDE2ID4gMS4yNSAqIGFtaS4yMDE2KSB+ICJoZWFsdGh5IiwNCiAgICAgIChobXQudGllciAhPSAib3RoZXIiKSAmIA0KICAgICAgICAoTWVkaWFuX0hvdXNlaG9sZF9JbmNvbWUuMjAxNiA8IDAuNzUgKiBhbWkuMjAxNikgfiAiZGlzdHJlc3NlZCIsDQogICAgICAoaG10LnRpZXIgPT0gIm90aGVyIikgfiBOQV9jaGFyYWN0ZXJfLA0KICAgICAgVFJVRSB+ICJtaWRkbGUiDQogICAgKQ0KICApDQpgYGANCg0KIyBNaWRkbGUgTmVpZ2hib3Job29kcyAtIEhNVCBEZWZpbml0b24NCg0KVGhpcyBtYXAgc2hvd3Mgb25seSBtaWRkbGUgbmVpZ2hib3Job29kcyBiYXNlZCBvbiBITVQgKGluY2x1ZGVzIG9ubHkgdHlwb2xvZ2llcyBEIHRocm91Z2ggSCkuIEJsYWNrIGJvcmRlcnMgYW5kIGxhYmVscy1vbi1ob3ZlciBhcmUgZm9yIG5laWdoYm9yaG9vZHMuIEF0IGZpcnN0IGdsYW5jZSwgdGhpcyByZW1vdmVzIG5laWdoYm9yaG9vZHMgd2l0aGluIHRoZSAid2hpdGUgTCIgYW5kICJibGFjayBidXR0ZXJmbHkiIG5laWdoYm9yaG9vZHMgb2YgQmFsdGltb3JlLCB3aXRoIG1vc3QgZXZlcnl0aGluZyBlbHNlIHJlbWFpbmluZy4NCg0KYGBge3J9DQptaWQuaG9vZHMuaG10IDwtIHN1YnNldChobXQsIGhtdC50aWVyID09ICJtaWRkbGUiKQ0KDQpsZWFmbGV0KCkgJT4lIA0KICBzZXRWaWV3KGxuZyA9IC03Ni42LCBsYXQgPSAzOS4zLCB6b29tID0gMTEpICU+JQ0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRTdGFtZW4uVG9uZXJMaXRlKSAlPiUNCiAgYWRkUG9seWdvbnMoZGF0YSA9IG1pZC5ob29kcy5obXQsIA0KICAgICAgICAgICAgICBjb2xvciA9IGl0ZWFtLmNvbG9yc1sxXSwNCiAgICAgICAgICAgICAgd2VpZ2h0ID0gMSwNCiAgICAgICAgICAgICAgI2ZpbGxDb2xvciA9IH5wYWwoTVZBMTdIcmRDZCksDQogICAgICAgICAgICAgIGZpbGxDb2xvciA9IGl0ZWFtLmNvbG9yc1sxXSwNCiAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAuNiwNCiAgICAgICAgICAgICAgI29wYWNpdHkgPSAwLA0KICAgICAgICAgICAgICBsYWJlbCA9IH5hcy5jaGFyYWN0ZXIobWlkLmhvb2RzLmhtdCRNVkExN0hyZENkKQ0KICAgICAgICAgICAgICApICU+JQ0KICAjYWRkR2VvSlNPTihob29kcywgd2VpZ2h0ID0gMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsT3BhY2l0eSA9IDApDQogIGFkZFBvbHlnb25zKGRhdGEgPSBob29kcywgDQogICAgICAgICAgICAgIHdlaWdodCA9IDIsIA0KICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsDQogICAgICAgICAgICAgIG9wYWNpdHkgPSAwLjUsDQogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMCwgDQogICAgICAgICAgICAgIGxhYmVsID0gfmhvb2RzJGxhYmVsKQ0KYGBgDQoNCkJyZWFraW5nICJtaWRkbGUiIGludG8gImxvd2VyIG1pZGRsZSIgKEYsIEcsIEgpIGFuZCAidXBwZXIgbWlkZGxlIiAoRCwgRSkgYW5kIGluY2x1ZGluZyAiaGVhbHRoeSIgYW5kICJkaXN0cmVzc2VkOg0KDQpgYGB7cn0NCiNtaWQuaG9vZHMuaG10LjJtaWQgPC0gc3Vic2V0KGhtdCwgaG10LnRpZXIuMm1pZCAlaW4lIGMoImxvd2VyIG1pZGRsZSIsICJ1cHBlciBtaWRkbGUiKSkNCg0KaG10LjJtaWQucGFsIDwtIGNvbG9yRmFjdG9yKA0KICBkb21haW4gPSBobXQkaG10LnRpZXIuMm1pZCwNCiAgcGFsZXR0ZSA9IGMoaXRlYW0uY29sb3JzWzRdLA0KICAgICAgICAgICAgICBpdGVhbS5jb2xvcnNbM10sDQogICAgICAgICAgICAgICIjZjRkNTdmIiwgDQogICAgICAgICAgICAgIE5BLA0KICAgICAgICAgICAgICBpdGVhbS5jb2xvcnNbMV0pDQopDQoNCmxlYWZsZXQoKSAlPiUgDQogIHNldFZpZXcobG5nID0gLTc2LjYsIGxhdCA9IDM5LjMsIHpvb20gPSAxMSkgJT4lDQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJFN0YW1lbi5Ub25lckxpdGUpICU+JQ0KICBhZGRQb2x5Z29ucyhkYXRhID0gaG10LCANCiAgICAgICAgICAgICAgY29sb3IgPSBpdGVhbS5jb2xvcnNbMV0sDQogICAgICAgICAgICAgIHdlaWdodCA9IDEsDQogICAgICAgICAgICAgIGZpbGxDb2xvciA9IH5obXQuMm1pZC5wYWwoaG10LnRpZXIuMm1pZCksDQogICAgICAgICAgICAgICNmaWxsQ29sb3IgPSBpdGVhbS5jb2xvcnNbMV0sDQogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gLjYsDQogICAgICAgICAgICAgICNvcGFjaXR5ID0gMCwNCiAgICAgICAgICAgICAgbGFiZWwgPSB+YXMuY2hhcmFjdGVyKGhtdCRNVkExN0hyZENkKQ0KICAgICAgICAgICAgICApICU+JQ0KICAjYWRkR2VvSlNPTihob29kcywgd2VpZ2h0ID0gMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsT3BhY2l0eSA9IDApDQogIGFkZFBvbHlnb25zKGRhdGEgPSBob29kcywgDQogICAgICAgICAgICAgIHdlaWdodCA9IDIsIA0KICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsDQogICAgICAgICAgICAgIG9wYWNpdHkgPSAwLjUsDQogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMCwgDQogICAgICAgICAgICAgIGxhYmVsID0gfmhvb2RzJGxhYmVsKQ0KYGBgDQoNCg0KIyBNaWRkbGUgTmVpZ2hib3Job29kcyAtIE1lZGlhbiBJbmNvbWUgRGVmaW5pdGlvbg0KDQpUaGlzIG1hcCBzaG93cyBvbmx5IG1pZGRsZSBuZWlnaGJvcmhvb2RzIGJhc2VkIG9uIHRoZSBtZWRpYW4gaW5jb21lIGRlZmluaXRpb24gLSB0aGF0IGlzLCBibG9jayBncm91cHMgd2hlcmUgdGhlIGhvdXNlaG9sZCBtZWRpYW4gaW5jb21lIGlzIGJldHdlZW4gNzUlIGFuZCAxMjUlIG9mIHRoZSBhcmVhIG1lZGlhbiBpbmNvbWUgKFtCYWx0aW1vcmUtQ29sdW1iaWEtVG93c29uIGZvciAyMDE2XShodHRwczovL3d3dy5odWR1c2VyLmdvdi9wb3J0YWwvZGF0YXNldHMvaWwuaHRtbCMyMDE2KSkuIFF1YWxpdGF0aXZlbHksIHRoaXMgcmVzdWx0cyBpbiBhIG1vcmUgc3BhcnNlIGFuZCBkaXNqb2ludGVkIGRlZmluaXRpb24gb2YgbWlkZGxlIG5laWdoYm9yaG9vZHMuIA0KDQpgYGB7cn0NCm1pZC5ob29kcy5tZWQuaW5jIDwtIHN1YnNldChobXQsIG1lZC5pbmMudGllciA9PSAibWlkZGxlIikNCg0KbGVhZmxldCgpICU+JSANCiAgc2V0VmlldyhsbmcgPSAtNzYuNiwgbGF0ID0gMzkuMywgem9vbSA9IDExKSAlPiUNCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkU3RhbWVuLlRvbmVyTGl0ZSkgJT4lDQogIGFkZFBvbHlnb25zKGRhdGEgPSBtaWQuaG9vZHMubWVkLmluYywgDQogICAgICAgICAgICAgIGNvbG9yID0gaXRlYW0uY29sb3JzWzNdLA0KICAgICAgICAgICAgICB3ZWlnaHQgPSAxLA0KICAgICAgICAgICAgICAjZmlsbENvbG9yID0gfnBhbChNVkExN0hyZENkKSwNCiAgICAgICAgICAgICAgZmlsbENvbG9yID0gaXRlYW0uY29sb3JzWzNdLA0KICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IC42LA0KICAgICAgICAgICAgICAjb3BhY2l0eSA9IDAsDQogICAgICAgICAgICAgIGxhYmVsID0gfmFzLmNoYXJhY3RlcihtaWQuaG9vZHMubWVkLmluYyRNVkExN0hyZENkKQ0KICAgICAgICAgICAgICApICU+JQ0KICAjYWRkR2VvSlNPTihob29kcywgd2VpZ2h0ID0gMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsT3BhY2l0eSA9IDApDQogIGFkZFBvbHlnb25zKGRhdGEgPSBob29kcywgDQogICAgICAgICAgICAgIHdlaWdodCA9IDIsIA0KICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsDQogICAgICAgICAgICAgIG9wYWNpdHkgPSAwLjUsDQogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMCwgDQogICAgICAgICAgICAgIGxhYmVsID0gfmhvb2RzJGxhYmVsKQ0KYGBgDQoNCiMgQ29tcGFyaXNvbiBvZiBEZWZpbml0aW9ucw0KDQpUaGUgbWFwIGJlbG93IG92ZXJsYXlzIHRoZSB0d28gZGVmaW5pdGlvbnMuDQoNCmBgYHtyfQ0KbGVhZmxldCgpICU+JSANCiAgc2V0VmlldyhsbmcgPSAtNzYuNiwgbGF0ID0gMzkuMywgem9vbSA9IDExKSAlPiUNCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkU3RhbWVuLlRvbmVyTGl0ZSkgJT4lDQogIGFkZFBvbHlnb25zKGRhdGEgPSBtaWQuaG9vZHMubWVkLmluYywgDQogICAgICAgICAgICAgIGNvbG9yID0gaXRlYW0uY29sb3JzWzNdLA0KICAgICAgICAgICAgICB3ZWlnaHQgPSAxLA0KICAgICAgICAgICAgICAjZmlsbENvbG9yID0gfnBhbChNVkExN0hyZENkKSwNCiAgICAgICAgICAgICAgZmlsbENvbG9yID0gaXRlYW0uY29sb3JzWzNdLA0KICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IC43LA0KICAgICAgICAgICAgICAjb3BhY2l0eSA9IDAsDQogICAgICAgICAgICAgIGxhYmVsID0gfmFzLmNoYXJhY3RlcihtaWQuaG9vZHMubWVkLmluYyRNVkExN0hyZENkKSkgJT4lDQogIGFkZFBvbHlnb25zKGRhdGEgPSBtaWQuaG9vZHMuaG10LCANCiAgICAgICAgICAgICAgY29sb3IgPSBpdGVhbS5jb2xvcnNbMV0sDQogICAgICAgICAgICAgIHdlaWdodCA9IDEsDQogICAgICAgICAgICAgICNmaWxsQ29sb3IgPSB+cGFsKE1WQTE3SHJkQ2QpLA0KICAgICAgICAgICAgICBmaWxsQ29sb3IgPSBpdGVhbS5jb2xvcnNbMV0sDQogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gLjUsDQogICAgICAgICAgICAgICNvcGFjaXR5ID0gMCwNCiAgICAgICAgICAgICAgbGFiZWwgPSB+YXMuY2hhcmFjdGVyKG1pZC5ob29kcy5obXQkTVZBMTdIcmRDZCkpICU+JQ0KICBhZGRQb2x5Z29ucyhkYXRhID0gaG9vZHMsIA0KICAgICAgICAgICAgICB3ZWlnaHQgPSAyLCANCiAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLA0KICAgICAgICAgICAgICBvcGFjaXR5ID0gMC41LA0KICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAsIA0KICAgICAgICAgICAgICBsYWJlbCA9IH5ob29kcyRsYWJlbCkgJT4lDQogIGFkZExlZ2VuZCgiYm90dG9tcmlnaHQiLA0KICAgICAgICAgICAgY29sb3JzID0gYyhpdGVhbS5jb2xvcnNbMV0sIGl0ZWFtLmNvbG9yc1szXSksDQogICAgICAgICAgICBsYWJlbHMgPSBjKCJITVQiLCAiTWVkaWFuIEluY29tZSIpKQ0KYGBgDQoNCkJlbG93IGlzIGEgdGFibGUgY29tcGFyaW5nIHRoZSB0d28gZGVmaW5pdGlvbnMgYmFzZWQgb24gdGhlIG51bWJlciBvZiBibG9jayBncm91cHMgYW5kIHBvcHVsYXRpb24gaW4gZWFjaCB0aWVyLiBBYm91dCA1NSUgb2YgQmFsdGltb3JlIHJlc2lkZW50cyBsaXZlIGluIGEgbWlkZGxlIG5laWdoYm9yaG9vZCBiYXNlZCBvbiB0aGUgSE1UIGRlZmluaXRpb24uIEhvd2V2ZXIsIG9ubHkgYWJvdXQgMTQlIG9mIHRoZSBjaXR5J3MgcG9wdWxhdGlvbiBtYWtlcyBiZXR3ZWVuIDc1JSBhbmQgMTI1JSBvZiBBTUkuICoqNzUlIG9mIHRoZSBjaXR5IHBvcHVsYXRpb24gbGl2ZXMgaW4gYSBibG9jayBncm91cCB3aXRoIG1lZGlhbiBpbmNvbWUgYmVsb3cgNzUlIG9mIEFNSS4qKiAoVXNlIHRoZSByaWdodCB0cmlhbmdsZSBhcnJvdyBhdCB0aGUgcmlnaHQgc2lkZSBvZiB0aGUgdGFibGUgaGVhZGluZyB0byBzZWUgYWRkaXRpb25hbCBjb2x1bW5zLikNCg0KYGBge3J9DQpobXQuc3VtbWFyeSA8LSBobXRAZGF0YSAlPiUgDQogIGdyb3VwX2J5KGhtdC50aWVyKSAlPiUNCiAgc3VtbWFyaXNlKG4uYmxvY2suZ3JvdXBzLmhtdCA9IG4oKSwNCiAgICAgICAgICAgIHBvcC5obXQgPSBzdW0oUG9wdWxhdGlvbi4yMDE2KSkgJT4lDQogIG11dGF0ZShwY3QuYmxvY2suZ3JvdXBzLmhtdCA9IHBlcmNlbnQobi5ibG9jay5ncm91cHMuaG10IC8gc3VtKG4uYmxvY2suZ3JvdXBzLmhtdCkpLA0KICAgICAgICAgcGN0LnBvcC5obXQgPSBwZXJjZW50KHBvcC5obXQgLyBzdW0ocG9wLmhtdCkpKQ0KDQptZWQuaW5jLnN1bW1tYXJ5IDwtIGhtdEBkYXRhICU+JSANCiAgZ3JvdXBfYnkobWVkLmluYy50aWVyKSAlPiUNCiAgc3VtbWFyaXNlKG4uYmxvY2suZ3JvdXBzLm1lZC5pbmMgPSBuKCksDQogICAgICAgICAgICBwb3AubWVkLmluYyA9IHN1bShQb3B1bGF0aW9uLjIwMTYpKSAlPiUNCiAgbXV0YXRlKHBjdC5ibG9jay5ncm91cHMubWVkLmluYyA9IHBlcmNlbnQobi5ibG9jay5ncm91cHMubWVkLmluYyAvIHN1bShuLmJsb2NrLmdyb3Vwcy5tZWQuaW5jKSksDQogICAgICAgICBwY3QucG9wLm1lZC5pbmMgPSBwZXJjZW50KHBvcC5tZWQuaW5jIC8gc3VtKHBvcC5tZWQuaW5jKSkpDQoNCmxlZnRfam9pbihobXQuc3VtbWFyeSwgbWVkLmluYy5zdW1tbWFyeSwgYnkgPSBjKCJobXQudGllciIgPSAibWVkLmluYy50aWVyIikpICU+JQ0KICByZW5hbWUodGllciA9ICJobXQudGllciIpICU+JQ0KICBmaWx0ZXIodGllciAhPSAib3RoZXIiKQ0KYGBgDQoNCg0KIyBJbmNvbWVzIGluIEhNVCBNaWRkbGUgTmVpZ2hib3Job29kcw0KDQpUaGUgdGFibGUgYmVsb3cgc2hvd3MgdGhlIGNvbWJpbmF0aW9uIG9mIG5laWdoYm9yaG9vZCBob3VzaW5nIG1hcmtldCB0aWVyIChiYXNlZCBvbiBITVQpIGFuZCBtZWRpYW4gaW5jb21lIGxldmVscyBpbiBlYWNoIG1hcmtldCB0aWVyIChiZWxvdyA3NSUgQU1JLCBiZXR3ZWVuIDc1JSBhbmQgMTI1JSBBTUksIG1vcmUgdGhhbiAxMjUlIEFNSSkuDQoNCkFNSSB1c2VkIGlzIFwkODYsNzAwIGJhc2VkIG9uIEhVRCdzIEZZMTYgZmlndXJlIGZvciBCYWx0aW1vcmUtQ29sdW1iaWEtVG93c29uLiBGb3IgRlkxOCwgdGhlIG51bWJlciBpcyBcJDk0LDQwMCwgaG93ZXZlciB0aGUgbWVkaWFuIGluY29tZSBkYXRhIGJlaW5nIHVzZWQgaW4gdGhpcyBhbmFseXNpcyBpcyB0aGUgNS15ZWFyIEFDUyBlc3RpbWF0ZXMgZW5kaW5nIGluIDIwMTYuDQoNCg0KDQpgYGB7cn0NCmhtdEBkYXRhIDwtIGhtdEBkYXRhICU+JSBtdXRhdGUoDQogIA0KICBpbmMuYnJhY2tldCA9IGNhc2Vfd2hlbigNCiAgICBNZWRpYW5fSG91c2Vob2xkX0luY29tZS4yMDE2ID4gMS4yNSAqIGFtaS4yMDE2IH4gImFib3ZlIDEyNSUgQU1JIiwNCiAgICBNZWRpYW5fSG91c2Vob2xkX0luY29tZS4yMDE2IDwgMC43NSAqIGFtaS4yMDE2IH4gImJlbG93IDc1JSBBTUkiLA0KICAgIFRSVUUgfiAiYmV0d2VlbiA3NSUgYW5kIDEyNSUgQU1JIiksDQogIA0KICB0aWVyLmluYy5icmFja2V0ID0gYXMuY2hhcmFjdGVyKHBhc3RlMChobXQudGllci4ybWlkLCAiIHwgIiwgaW5jLmJyYWNrZXQpKQ0KICANCikNCg0KaG10QGRhdGEgJT4lDQogIGdyb3VwX2J5KHRpZXIuaW5jLmJyYWNrZXQpICU+JQ0KICBzdW1tYXJpc2Uobi5ibG9jay5ncm91cHMgPSBuKCksDQogICAgICAgICAgICBwb3AgPSBzdW0oUG9wdWxhdGlvbi4yMDE2KSkgJT4lDQogIG11dGF0ZShwY3QuY2l0eS5wb3AgPSBwZXJjZW50KHBvcCAvIHN1bShwb3ApKSkNCmBgYA0KDQpJbnRlcmVzdGluZyB0byBub3RlIHRoYXQgYWxsIGhvdXNpbmcgbWFya2V0IHRpZXJzIGluY2x1ZGUgdGhlIGxvdyBhbmQgbWlkZGxlIG1lZGlhbiBpbmNvbWUgcmFuZ2VzLCBidXQgb25seSB0aGUgaGVhbHRoeSBITVQgdGllciBpbmNsdWRlcyBibG9jayBncm91cHMgd2l0aCBtZWRpYW4gaW5jb21lcyBvdmVyIDEyNSUgb2YgQU1JLg0KDQpIZXJlJ3MgYW4gYWxhcm1pbmcgbnVnZ2V0OiA1NSUgb2YgdGhlIGNpdHkgcG9wdWxhdGlvbiBpcyBpbiBhIG1pZGRsZSBuZWlnaGJvcmhvb2QgLCBhbmQgKio5MCUgb2YgdGhlIG1pZGRsZSBuZWlnaGJvcmhvb2QgcG9wdWxhdGlvbioqIGxpdmVzIGluIGEgYmxvY2sgZ3JvdXAgd2l0aCBhIG1lZGlhbiBpbmNvbWUgbGVzcyB0aGFuIDc1JSBBTUk6DQoNCmBgYHtyfQ0KaG10QGRhdGEgJT4lDQogIGZpbHRlcihobXQudGllciA9PSAibWlkZGxlIikgJT4lDQogIGdyb3VwX2J5KHRpZXIuaW5jLmJyYWNrZXQpICU+JQ0KICBzdW1tYXJpc2Uobi5ibG9jay5ncm91cHMgPSBuKCksDQogICAgICAgICAgICBwb3AgPSBzdW0oUG9wdWxhdGlvbi4yMDE2KSkgJT4lDQogIG11dGF0ZShwY3QgPSBwZXJjZW50KHBvcCAvIHN1bShwb3ApKSkNCmBgYA0KDQojIyMgTWlkZGxlIE5laWdoYm9yaG9vZHMgd2l0aCBMb3cgb3IgSGlnaCBJbmNvbWVzDQoNCihObyBtaWRkbGUgbmVpZ2hib3Job29kIGJsb2NrIGdyb3VwcyB3aXRoID4xMjUlIEFNSS4pDQoNCmBgYHtyfQ0KdGllci5pbmNvbWUuc3Vic2V0IDwtIHN1YnNldChobXQsICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG10LnRpZXIuMm1pZCAlaW4lIGMoImxvd2VyIG1pZGRsZSIsICJ1cHBlciBtaWRkbGUiKSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluYy5icmFja2V0ICVpbiUgYygiYWJvdmUgMTI1JSBBTUkiLCAiYmVsb3cgNzUlIEFNSSIpICkNCg0KdGllci5pbmMucGFsIDwtIGNvbG9yRmFjdG9yKA0KICBkb21haW4gPSB0aWVyLmluY29tZS5zdWJzZXQkdGllci5pbmMuYnJhY2tldCwNCiAgcGFsZXR0ZSA9IGMoaXRlYW0uY29sb3JzWzFdLA0KICAgICAgICAgICAgICBpdGVhbS5jb2xvcnNbM10pDQopDQoNCmxlYWZsZXQoKSAlPiUgDQogIHNldFZpZXcobG5nID0gLTc2LjYsIGxhdCA9IDM5LjMsIHpvb20gPSAxMSkgJT4lDQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJFN0YW1lbi5Ub25lckxpdGUpICU+JQ0KICBhZGRQb2x5Z29ucyhkYXRhID0gdGllci5pbmNvbWUuc3Vic2V0LCANCiAgICAgICAgICAgICAgY29sb3IgPSBpdGVhbS5jb2xvcnNbMl0sDQogICAgICAgICAgICAgIHdlaWdodCA9IDEsDQogICAgICAgICAgICAgIGZpbGxDb2xvciA9IH50aWVyLmluYy5wYWwodGllci5pbmMuYnJhY2tldCksDQogICAgICAgICAgICAgICNmaWxsQ29sb3IgPSBpdGVhbS5jb2xvcnNbMV0sDQogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMC43LA0KICAgICAgICAgICAgICAjb3BhY2l0eSA9IDAsDQogICAgICAgICAgICAgIGxhYmVsID0gfmFzLmNoYXJhY3RlcihwYXN0ZTAodGllci5pbmNvbWUuc3Vic2V0JHRpZXIuaW5jLmJyYWNrZXQpKQ0KICAgICAgICAgICAgICApICU+JQ0KICAjYWRkR2VvSlNPTihob29kcywgd2VpZ2h0ID0gMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsT3BhY2l0eSA9IDApDQogIGFkZFBvbHlnb25zKGRhdGEgPSBob29kcywNCiAgICAgICAgICAgICB3ZWlnaHQgPSAyLA0KICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICAgICBvcGFjaXR5ID0gMC41LA0KICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMCwNCiAgICAgICAgICAgICBsYWJlbCA9IH5ob29kcyRsYWJlbCkgJT4lDQogIGFkZExlZ2VuZCgiYm90dG9tcmlnaHQiLA0KICAgICAgICAgICAgY29sb3JzID0gYyhpdGVhbS5jb2xvcnNbMV0sIGl0ZWFtLmNvbG9yc1szXSksDQogICAgICAgICAgICBsYWJlbHMgPSBjKCJsb3dlciBtaWRkbGUgfCBiZWxvdyA3NSUgQU1JIiwgInVwcGVyIG1pZGRsZSB8IGJlbG93IDc1JSBBTUkiKSkNCmBgYA0KDQojIyMgSGVhbHRoeSBhbmQgRGlzdHJlc3NlZCBOZWlnaGJvcmhvb2RzIHdpdGggTW9kZXJhdGUgSW5jb21lDQoNCmBgYHtyfQ0KdGllci5pbmNvbWUuc3Vic2V0IDwtIHN1YnNldCgNCiAgaG10LCAgDQogIHRpZXIuaW5jLmJyYWNrZXQgJWluJSBjKCJkaXN0cmVzc2VkIHwgYmV0d2VlbiA3NSUgYW5kIDEyNSUgQU1JIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgImhlYWx0aHkgfCBiZXR3ZWVuIDc1JSBhbmQgMTI1JSBBTUkiKQ0KKQ0KDQp0aWVyLmluYy5wYWwgPC0gY29sb3JGYWN0b3IoDQogIGRvbWFpbiA9IHRpZXIuaW5jb21lLnN1YnNldCR0aWVyLmluYy5icmFja2V0LA0KICBwYWxldHRlID0gYyhpdGVhbS5jb2xvcnNbNF0sDQogICAgICAgICAgICAgIGl0ZWFtLmNvbG9yc1s1XSkNCikNCg0KbGVhZmxldCgpICU+JSANCiAgc2V0VmlldyhsbmcgPSAtNzYuNiwgbGF0ID0gMzkuMywgem9vbSA9IDExKSAlPiUNCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkU3RhbWVuLlRvbmVyTGl0ZSkgJT4lDQogIGFkZFBvbHlnb25zKGRhdGEgPSB0aWVyLmluY29tZS5zdWJzZXQsIA0KICAgICAgICAgICAgICBjb2xvciA9IGl0ZWFtLmNvbG9yc1syXSwNCiAgICAgICAgICAgICAgd2VpZ2h0ID0gMSwNCiAgICAgICAgICAgICAgZmlsbENvbG9yID0gfnRpZXIuaW5jLnBhbCh0aWVyLmluYy5icmFja2V0KSwNCiAgICAgICAgICAgICAgI2ZpbGxDb2xvciA9IGl0ZWFtLmNvbG9yc1sxXSwNCiAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjcsDQogICAgICAgICAgICAgICNvcGFjaXR5ID0gMCwNCiAgICAgICAgICAgICAgbGFiZWwgPSB+YXMuY2hhcmFjdGVyKHBhc3RlMCh0aWVyLmluY29tZS5zdWJzZXQkdGllci5pbmMuYnJhY2tldCkpDQogICAgICAgICAgICAgICkgJT4lDQogICNhZGRHZW9KU09OKGhvb2RzLCB3ZWlnaHQgPSAxLCBjb2xvciA9ICJibGFjayIsIGZpbGxPcGFjaXR5ID0gMCkNCiAgYWRkUG9seWdvbnMoZGF0YSA9IGhvb2RzLA0KICAgICAgICAgICAgIHdlaWdodCA9IDIsDQogICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLA0KICAgICAgICAgICAgIG9wYWNpdHkgPSAwLjUsDQogICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLA0KICAgICAgICAgICAgIGxhYmVsID0gfmhvb2RzJGxhYmVsKSAlPiUNCiAgYWRkTGVnZW5kKCJib3R0b21yaWdodCIsDQogICAgICAgICAgICBjb2xvcnMgPSBjKGl0ZWFtLmNvbG9yc1s0XSwgaXRlYW0uY29sb3JzWzVdKSwNCiAgICAgICAgICAgIGxhYmVscyA9IGMoImRpc3RyZXNzZWQgfCBiZXR3ZWVuIDc1JSBhbmQgMTI1JSBBTUkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgImhlYWx0aHkgfCBiZXR3ZWVuIDc1JSBhbmQgMTI1JSBBTUkiKSkNCmBgYA0KDQoNCiMjIyBIZWFsdGh5IE5laWdoYm9yaG9vZHMgd2l0aCBMb3cgTWVkaWFuIEluY29tZQ0KDQpgYGB7cn0NCnRpZXIuaW5jb21lLnN1YnNldCA8LSBzdWJzZXQoDQogIGhtdCwgIA0KICB0aWVyLmluYy5icmFja2V0ICVpbiUgYygiaGVhbHRoeSB8IGJlbG93IDc1JSBBTUkiKQ0KKQ0KDQp0aWVyLmluYy5wYWwgPC0gY29sb3JGYWN0b3IoDQogIGRvbWFpbiA9IHRpZXIuaW5jb21lLnN1YnNldCR0aWVyLmluYy5icmFja2V0LA0KICBwYWxldHRlID0gYyhpdGVhbS5jb2xvcnNbNV0pDQopDQoNCmxlYWZsZXQoKSAlPiUgDQogIHNldFZpZXcobG5nID0gLTc2LjYsIGxhdCA9IDM5LjMsIHpvb20gPSAxMSkgJT4lDQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJFN0YW1lbi5Ub25lckxpdGUpICU+JQ0KICBhZGRQb2x5Z29ucyhkYXRhID0gdGllci5pbmNvbWUuc3Vic2V0LCANCiAgICAgICAgICAgICAgY29sb3IgPSBpdGVhbS5jb2xvcnNbMl0sDQogICAgICAgICAgICAgIHdlaWdodCA9IDEsDQogICAgICAgICAgICAgIGZpbGxDb2xvciA9IH50aWVyLmluYy5wYWwodGllci5pbmMuYnJhY2tldCksDQogICAgICAgICAgICAgICNmaWxsQ29sb3IgPSBpdGVhbS5jb2xvcnNbMV0sDQogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMC43LA0KICAgICAgICAgICAgICAjb3BhY2l0eSA9IDAsDQogICAgICAgICAgICAgIGxhYmVsID0gfmFzLmNoYXJhY3RlcihwYXN0ZTAodGllci5pbmNvbWUuc3Vic2V0JHRpZXIuaW5jLmJyYWNrZXQpKQ0KICAgICAgICAgICAgICApICU+JQ0KICAjYWRkR2VvSlNPTihob29kcywgd2VpZ2h0ID0gMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsT3BhY2l0eSA9IDApDQogIGFkZFBvbHlnb25zKGRhdGEgPSBob29kcywNCiAgICAgICAgICAgICB3ZWlnaHQgPSAyLA0KICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICAgICBvcGFjaXR5ID0gMC41LA0KICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMCwNCiAgICAgICAgICAgICBsYWJlbCA9IH5ob29kcyRsYWJlbCkgJT4lDQogIGFkZExlZ2VuZCgiYm90dG9tcmlnaHQiLA0KICAgICAgICAgICAgY29sb3JzID0gYyhpdGVhbS5jb2xvcnNbNV0pLA0KICAgICAgICAgICAgbGFiZWxzID0gYygiaGVhbHRoeSB8IGJlbG93IDc1JSBBTUkiKSkNCg0KYGBgDQoNCg0KIyBDaGFyYWN0ZXJpc3RpY3Mgb2YgTWlkZGxlIE5laWdoYm9yaG9vZHMNCg0KVXNpbmcgdGhlIEhNVCBkZWZpbml0aW9uIGZvciBtaWRkbGUgbmVpZ2hib3Job29kcywgdGhlIGZvbGxvd2luZyBhcmUgbG9va3MgYXQgYmxvY2sgZ3JvdXAgY2hhcmFjdGVyaXN0aWNzIGdyb3VwZWQgYnkgdGllci4NCg0KQmVjYXVzZSB0aGUgSE1UIHVzZXMgYSBjbHVzdGVyIGFuYWx5c2lzIHRoYXQgaW5jbHVkZXMgc2V2ZXJhbCBob3VzaW5nIHZhcmlhYmxlcywgd2UnZCBleHBlY3QgdGhlcmUgdG8gYmUgYW4gb3ZlcmxhcCBpbiB0aGUgbWVkaWFuIGluY29tZSBkaXN0cmlidXRpb25zIGJlY2F1c2UgdGhlIHRpZXJzIHdlcmUgbm90IGRlZmluZWQgYnkgbWVkaWFuIGluY29tZS4NCg0KYGBge3J9DQpobXRAZGF0YSAlPiUgDQogIGZpbHRlcihobXQudGllciAhPSAib3RoZXIiLA0KICAgICAgICAgIWlzLm5hKE1lZGlhbl9Ib3VzZWhvbGRfSW5jb21lLjIwMTYpKSAlPiUgDQogIGdncGxvdChhZXMoTWVkaWFuX0hvdXNlaG9sZF9JbmNvbWUuMjAxNikpICsNCiAgZ2VvbV9mcmVxcG9seShhZXMoY29sb3IgPSBobXQudGllciksIGJpbnMgPSA1MCkgKw0KICB0aGVtZV9pdGVhbV9wcmVzZW50YXRpb25zKCkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gcGFzdGUwKCJCbG9jayBHcm91cCBNZWRpYW4gSG91c2Vob2xkIEluY29tZVxuIiwNCiAgICAgICAgICAgICAgICAgICAiRGlzdHJpYnV0aW9uIGJ5IEhNVCBNYXJrZXQgdGllciIpLA0KICAgIHN1YnRpdGxlID0gIiIsDQogICAgeCA9ICJNZWRpYW4gSG91c2Vob2xkIEluY29tZSIsDQogICAgeSA9ICJDb3VudCIsDQogICAgY2FwdGlvbiA9IHBhc3RlMCgidGllciBiYXNlZCBvbiAyMDE3IEhNVCBhbmFseXNpczsgIiwNCiAgICAgICAgICAgICAgICAgICAgICJtZWRpYW4gaG91c2Vob2xkIGluY29tZXMgZnJvbSAyMDEyLTIwMTYgQUNTIGRhdGEuIikNCiAgKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBhbWkuMjAxNiksIGNvbG9yID0gaXRlYW0uY29sb3JzWzJdKSArDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IGFtaS4yMDE2ICsgMjUwMCwgeSA9IDI1LCANCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZTAoIkJhbHRpbW9yZS1Db2x1bWJpYS1Ub3dzb24gMjAxNiBBTUk6XG4iLCBkb2xsYXIoYW1pLjIwMTYpKSwNCiAgICAgICAgICAgaGp1c3QgPSAwKSArDQogIHRoZW1lKHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEsIGZhY2UgPSAicGxhaW4iKSkNCmBgYA0KDQpEaWZmZXJlbmNlIGluIG1lZGlhbiBpbmNvbWVzIGJldHdlZW4gdXBwZXIgYW5kIGxvd2VyIG1pZGRsZSBuZWlnaGJvcmhvb2RzLg0KDQpgYGB7cn0NCmhtdEBkYXRhICU+JSANCiAgZmlsdGVyKGhtdC50aWVyICE9ICJvdGhlciIsDQogICAgICAgICAhaXMubmEoTWVkaWFuX0hvdXNlaG9sZF9JbmNvbWUuMjAxNiksDQogICAgICAgICBobXQudGllciA9PSAibWlkZGxlIikgJT4lIA0KICBnZ3Bsb3QoYWVzKE1lZGlhbl9Ib3VzZWhvbGRfSW5jb21lLjIwMTYpKSArDQogIGdlb21fZnJlcXBvbHkoYWVzKGNvbG9yID0gaG10LnRpZXIuMm1pZCksIGJpbnMgPSA1MCkgKw0KICB0aGVtZV9pdGVhbV9wcmVzZW50YXRpb25zKCkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gcGFzdGUwKCJNaWRkbGUgTmVpZ2hib3Job29kXG5CbG9jayBHcm91cCBNZWRpYW4gSG91c2Vob2xkIEluY29tZVxuIiwNCiAgICAgICAgICAgICAgICAgICAiRGlzdHJpYnV0aW9uIGJ5IEhNVCBNYXJrZXQgdGllciIpLA0KICAgIHN1YnRpdGxlID0gIiIsDQogICAgeCA9ICJNZWRpYW4gSG91c2Vob2xkIEluY29tZSIsDQogICAgeSA9ICJDb3VudCIsDQogICAgY2FwdGlvbiA9IHBhc3RlMCgidGllciBiYXNlZCBvbiAyMDE3IEhNVCBhbmFseXNpczsgIiwNCiAgICAgICAgICAgICAgICAgICAgICJtZWRpYW4gaG91c2Vob2xkIGluY29tZXMgZnJvbSAyMDEyLTIwMTYgQUNTIGRhdGEuIikNCiAgKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBhbWkuMjAxNiksIGNvbG9yID0gaXRlYW0uY29sb3JzWzJdKSArDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IGFtaS4yMDE2IC0gMjUwMCwgeSA9IDE2LCANCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZTAoIkJhbHRpbW9yZS1Db2x1bWJpYS1Ub3dzb24gMjAxNiBBTUk6XG4iLCBkb2xsYXIoYW1pLjIwMTYpKSwNCiAgICAgICAgICAgaGp1c3QgPSAxKSArDQogIHRoZW1lKHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEsIGZhY2UgPSAicGxhaW4iKSwNCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQ0KYGBgDQoNCk1pZGRsZSBuZWlnaGJvcmhvb2RzIGRvIHNlZW0gdG8gc2tldyB0b3dhcmRzIG9sZGVyIGhvbWVvd25lcnNoaXAuDQoNCmBgYHtyfQ0KaG10QGRhdGEgJT4lIA0KICBmaWx0ZXIoaG10LnRpZXIgIT0gIm90aGVyIikgJT4lIA0KICBnZ3Bsb3QoYWVzKFBlcmNlbnRfSG9tZW93bmVyX092ZXJfNjUuMjAxNikpICsNCiAgZ2VvbV9mcmVxcG9seShhZXMoY29sb3IgPSBobXQudGllciksIHN0YXQgPSAiZGVuc2l0eSIpICsNCiAgdGhlbWVfaXRlYW1fcHJlc2VudGF0aW9ucygpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICIlIEhvbWVvd25lcnMgNjUrIGJ5IEhNVCBNYXJrZXQgdGllciIsDQogICAgc3VidGl0bGUgPSAiIiwNCiAgICB4ID0gIiUgSG9tZW93bmVycyA2NSsgeS5vLiIsDQogICAgeSA9ICJEZW5zaXR5IiwNCiAgICBjYXB0aW9uID0gcGFzdGUwKCJ0aWVyIGJhc2VkIG9uIDIwMTcgSE1UIGFuYWx5c2lzOyAiLA0KICAgICAgICAgICAgICAgICAgICAgIiUgaG9tZW93bmVycyA2NSBhbmQgb3ZlciBmcm9tIDIwMTItMjAxNiBBQ1MgZGF0YS4iKQ0KICApICsNCiAgdGhlbWUocGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSwgZmFjZSA9ICJwbGFpbiIpKQ0KYGBgDQoNCkJlbG93IGlzIHRoZSBhdmVyYWdlIGJsb2NrIGdyb3VwIHBlcmNlbnRhZ2Ugb2YgaG9tZW93bmVycyB0aGF0IGFyZSBvdmVyIHRoZSBhZ2Ugb2YgNjUuDQoNCmBgYHtyfQ0KaG10QGRhdGEgJT4lDQogIGZpbHRlcihobXQudGllciAhPSAib3RoZXIiKSAlPiUNCiAgZ3JvdXBfYnkoaG10LnRpZXIpICU+JQ0KICBzdW1tYXJpc2UobWVhbi5wY3QuaG8ub3ZlcjY1ID0gcGVyY2VudChtZWFuKFBlcmNlbnRfSG9tZW93bmVyX092ZXJfNjUuMjAxNikpLA0KICAgICAgICAgICAgbWVkLnBjdC5oby5vdmVyNjUgPSBwZXJjZW50KG1lZGlhbihQZXJjZW50X0hvbWVvd25lcl9PdmVyXzY1LjIwMTYpKSkNCmBgYA0KDQpXaGlsZSBhYm91dCA1NCUgb2YgdGhlIGNpdHkgcG9wdWxhdGlvbiBsaXZlcyBpbiBtaWRkbGUgbmVpZ2hib3Job29kcywgNjUlIG9mIHRoZSBob3VzZWhvbGRzIHdpdGggYSBob21lb3duZXIgNjUgeWVhcnMgb2YgYWdlIG9yIG9sZGVyIGxpdmUgaW4gYSBtaWRkbGUgbmVpZ2hib3Job29kLg0KDQpgYGB7cn0NCmhtdEBkYXRhICU+JQ0KICBmaWx0ZXIoaG10LnRpZXIgIT0gIm90aGVyIikgJT4lDQogIG11dGF0ZShoaCA9IFBvcHVsYXRpb24uMjAxNiAvIEhvdXNlaG9sZF9TaXplLjIwMTYsDQogICAgICAgICBoaC5vd24gPSBoaCAqIFBlcmNlbnRfT3duZXIuMjAxNiwNCiAgICAgICAgIGhoLm93bmVyLm92ZXI2NSA9IGhoLm93biAqIFBlcmNlbnRfSG9tZW93bmVyX092ZXJfNjUuMjAxNikgJT4lDQogIGdyb3VwX2J5KGhtdC50aWVyKSAlPiUNCiAgc3VtbWFyaXNlKGhoLm93bmVyLm92ZXI2NSA9IHN1bShoaC5vd25lci5vdmVyNjUpKSAlPiUNCiAgbXV0YXRlKHBjdC5jaXR5Lm93bmVyLm92ZXI2NSA9IHBlcmNlbnQoaGgub3duZXIub3ZlcjY1IC8gc3VtKGhoLm93bmVyLm92ZXI2NSkpKQ0KYGBgDQpgYGB7cn0NCmxtLmRhdGEgPC0gaG10QGRhdGEgJT4lIGZpbHRlcihNZWRpYW5fSG91c2Vob2xkX0luY29tZS4yMDE2ICE9IDApDQpsbS5tb2RlbCA8LSBsbShNU1AxNTE3ZW8gfiBNZWRpYW5fSG91c2Vob2xkX0luY29tZS4yMDE2LCBkYXRhID0gbG0uZGF0YSkNCg0KbG0ubW9kZWwNCg0KbGlicmFyeShwbG90bHkpDQoNCmluYy5wcmljZSA8LSBobXRAZGF0YSAlPiUNCiAgZ2dwbG90KGFlcyhNZWRpYW5fSG91c2Vob2xkX0luY29tZS4yMDE2LCBNU1AxNTE3ZW8pKSArDQogIGdlb21fcG9pbnQoKSArDQogIHRoZW1lX2l0ZWFtX3ByZXNlbnRhdGlvbnMoKSArDQogIGdlb21fYWJsaW5lKGFlcyhpbnRlcmNlcHQgPSBsbS5tb2RlbCRjb2VmZmljaWVudHNbMV0sIA0KICAgICAgICAgICAgICAgICAgc2xvcGUgPSBsbS5tb2RlbCRjb2VmZmljaWVudHNbMl0pKSArDQogIHhsYWIoIk1lZGlhbiBIb3VzZWhvbGQgSW5jb21lIikgKw0KICB5bGFiKCJNZWRpYW4gU2FsZXMgUHJpY2UiKQ0KDQpnZ3Bsb3RseShpbmMucHJpY2UpDQogIA0KDQpzdW1tYXJ5KGxtLm1vZGVsKQ0KYGBgDQoNCg0KDQoNCg==